﻿using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;

using Microsoft.Devices.Sensors;
using Microsoft.Phone.Controls;

namespace wpProtractor
{
	public partial class MainPage : PhoneApplicationPage
	{
		public MainPage()
		{
			InitializeComponent();

			SupportedOrientations = SupportedPageOrientation.Portrait;
			
			Loaded += MainPage_Loaded;

			OrientationChanged += new EventHandler<OrientationChangedEventArgs>(MainPage_OrientationChanged);
		}

		void MainPage_OrientationChanged(object sender, OrientationChangedEventArgs e)
		{
			checkOrientation();
		}

		private void checkOrientation()
		{
			var isPortrait = Orientation == PageOrientation.PortraitUp || Orientation == PageOrientation.PortraitDown;
			ActualHeightBind = (isPortrait) ? ActualHeight : ActualWidth;
			ActualWidthBind = (isPortrait) ? ActualWidth : ActualHeight;

			UpdateLayoutCanvases();
		}

		void MainPage_Loaded(object sender, RoutedEventArgs e)
		{
			DataContext = this;
			checkOrientation();
			var sensor = new AccelerometerSensor();
			sensor.ReadingChanged += sensor_ReadingChanged;
			sensor.Start();
		}


		void sensor_ReadingChanged(object sender, AccelerometerReadingAsyncEventArgs e)
		{
			Dispatcher.BeginInvoke(() =>
			                       	{
										var isPortrait = Orientation == PageOrientation.PortraitUp || Orientation == PageOrientation.PortraitDown;

										var data = e.Value.Value;
										
										var top = (isPortrait) ? data.Y : data.X;
										var side = (isPortrait) ? data.X : data.Y;

										double pitch, roll;
										CalculatePitchAndRoll(top, side, data.Z, out pitch, out roll);
										PitchAngle = pitch;
										RollAngle = roll;
			                       		UpdateLayoutCanvases();
			                       	});
		}

		private static void CalculatePitchAndRoll(double top, double side, double through, out double pitch, out double roll)
		{
			pitch = CalculateAngle(side, top);
			roll = CalculateAngle(top, through);

			if (top < 0)
			{
				// left side Y is positive
				pitch += (side > 0) ? -180 : 180;
				roll *= -1;
			}

			roll -= 90;
		}

		private static double CalculateAngle(double y, double x)
		{
			if (y != 0 && x != 0)
				return Math.Atan2(y, x) * 180 / Math.PI;

			return 0;
		}

		public double ActualHeightBind
		{
			get { return (double)GetValue(ActualHeightBindProperty); }
			set { SetValue(ActualHeightBindProperty, value); }
		}

		// Using a DependencyProperty as the backing store for ActualHeightBind.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty ActualHeightBindProperty =
			DependencyProperty.Register("ActualHeightBind", typeof(double), typeof(MainPage), null);


		public double ActualWidthBind
		{
			get { return (double)GetValue(ActualWidthBindProperty); }
			set { SetValue(ActualWidthBindProperty, value); }
		}

		// Using a DependencyProperty as the backing store for ActualHeightBind.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty ActualWidthBindProperty =
			DependencyProperty.Register("ActualWidthBind", typeof(double), typeof(MainPage), null);
		
		public double PitchAngle
		{
			get { return (double)GetValue(PitchAngleProperty); }
			set { SetValue(PitchAngleProperty, value); }
		}

		// Using a DependencyProperty as the backing store for PitchAngle.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty PitchAngleProperty =
			DependencyProperty.Register("PitchAngle", typeof(double), typeof(MainPage), new PropertyMetadata(0.0));


		public double RollAngle
		{
			get { return (double)GetValue(RollAngleProperty); }
			set { SetValue(RollAngleProperty, value); }
		}

		// Using a DependencyProperty as the backing store for PitchAngle.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty RollAngleProperty =
			DependencyProperty.Register("RollAngle", typeof(double), typeof(MainPage), new PropertyMetadata(0.0));


		private void UpdateLayoutCanvases()
		{
			UpdateRotateTransform();
			UpdateAngles();

			currAngle.Text = PitchAngle.ToString();
			Roll.Text = RollAngle.ToString();
			Pitch.Text = PitchAngle.ToString();
		}

		private void UpdateRotateTransform()
		{
			var rt = GetRotateTransform(rotateGrid);

			rt.Angle = PitchAngle;
			rt.CenterX = HalfConverter.ReturnValue(ActualWidthBind);
			rt.CenterY = HalfConverter.ReturnValue(ActualHeightBind);

			rotateGrid.RenderTransform = rt;
		}

		private const double RadianConv = Math.PI / 180.0;
		private void UpdateAngles()
		{
			var tt = GetTranslateTransform(angleCanvas);
			tt.X = HalfConverter.ReturnValue(ActualWidthBind);
			tt.Y = HalfConverter.ReturnValue(ActualHeightBind);

			angleCanvas.RenderTransform = tt;
			var angleRadians = (PitchAngle) * RadianConv;
			var widthLength = ActualWidthBind * .25;
			var heightLength = ActualHeightBind * .25;

			

			// http://forums.xkcd.com/viewtopic.php?f=17&t=51871
			var rPrime = (widthLength * heightLength) /
						 (Math.Sqrt(Math.Pow(heightLength * Math.Cos(angleRadians), 2) +
									Math.Pow(widthLength * Math.Sin(angleRadians), 2)));

			primaryAngleAdjacent.Point = new Point(-1.0 * widthLength, 0);
			primaryAngleHypotenuse.Size = new Size(widthLength, heightLength);

			primaryAngleHypotenuse.Point = new Point(
				-1.0 * rPrime * Math.Sin((90 - PitchAngle) * RadianConv), -1.0 * rPrime * Math.Cos((90 - PitchAngle) * RadianConv));
		}

		private static TranslateTransform GetTranslateTransform(UIElement target)
		{
			TranslateTransform tt;
			// check if rotateGrid is a group
			if (target.RenderTransform is TranslateTransform)
				tt = target.RenderTransform as TranslateTransform;
			else
				tt = new TranslateTransform();

			return tt;
		}
		private static RotateTransform GetRotateTransform(UIElement target)
		{
			RotateTransform tt;
			// check if rotateGrid is a group
			if (target.RenderTransform is RotateTransform)
				tt = target.RenderTransform as RotateTransform;
			else
				tt = new RotateTransform();

			return tt;
		}

		
	}

	public class DoubleConverter : IValueConverter
	{
		public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
		{
			return ReturnValue((double)value);
		}

		public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
		{
			throw new NotImplementedException();
		}

		public static double ReturnValue(double value)
		{
			return value * 2.0;
		}
	}
	public class HalfConverter : IValueConverter
	{
		public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
		{
			return ReturnValue((double)value);
		}

		public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
		{
			throw new NotImplementedException();
		}

		public static double ReturnValue(double value)
		{
			return value * .5;
		}
	}

	public class NegativeDoubleConverter : IValueConverter
	{
		public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
		{
			return ReturnValue((double)value);
		}

		public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
		{
			throw new NotImplementedException();
		}

		public static double ReturnValue(double value)
		{
			return -1.0 * DoubleConverter.ReturnValue(value);
		}
	}
}